#include "emac_internal.h"
#include "emac_rx.h"
#include "emac_tx.h"
#include "emac.h"
#include "semaphores.h"
#include "interrupt_controller.h"
#include "os_io.h"
#include "ksem.h"

static uint8_t g_device_mac_addr[6] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x00};
static sem_t *emac_rx_sem = NULL;
static emac_rx_ctx_t *rx_ctx = NULL;
static emac_tx_ctx_t *tx_ctx = NULL;

void initialize_emac_interrupts(void)
{
    enable_interrupt(AINTC_EMAC_C0_RX_THRES_INTERRUPT);
    //enable_interrupt(AINTC_EMAC_C1_RX_THRES_INTERRUPT);
    enable_interrupt(AINTC_EMAC_C0_RX_INTERRUPT);
    //enable_interrupt(AINTC_EMAC_C1_RX_INTERRUPT);
    enable_interrupt(AINTC_EMAC_C0_TX_INTERRUPT);
    //enable_interrupt(AINTC_EMAC_C1_TX_INTERRUPT);

    return;
}


void emac_cfg(uint32_t data)
{
    emac_cfg_t *cfg = (emac_cfg_t *)data;
    //printf("EMAC cfg called\n");

    if (cfg->emac_rx_sem) {
        emac_rx_sem = cfg->emac_rx_sem;
    }

    if (cfg->rx_ctx) {
        rx_ctx = cfg->rx_ctx;
	//printf("rx_ctx values: head:0x%x, tail:0x%x\n", rx_ctx->head, rx_ctx->tail);
    }

    if (cfg->tx_ctx) {
        tx_ctx = cfg->tx_ctx;
    }

    return;
}

void emac_init(void)
{
    uint32_t i = 0;
    uint32_t temp = 0;

    // Disable emac module interrupts
    C0RXTHRESHEN_REG = 0;
    C1RXTHRESHEN_REG = 0;
    C2RXTHRESHEN_REG = 0;
    C0RXEN_REG = 0;
    C1RXEN_REG = 0;
    C2RXEN_REG = 0;
    C0TXEN_REG = 0;
    C1TXEN_REG = 0;
    C2TXEN_REG = 0;
    C0MISCEN_REG = 0;
    C1MISCEN_REG = 0;
    C2MISCEN_REG = 0;

    MACCONTROL_REG = 0;
    RXCONTROL_REG = 0;
    TXCONTROL_REG = 0;

    /*
    * Head pointer registers for RX/TX descriptors
    */
    RX0HDP_REG = 0;
    RX1HDP_REG = 0;
    RX2HDP_REG = 0;
    RX3HDP_REG = 0;
    RX4HDP_REG = 0;
    RX5HDP_REG = 0;
    RX6HDP_REG = 0;
    RX7HDP_REG = 0;
    TX0HDP_REG = 0;
    TX1HDP_REG = 0;
    TX2HDP_REG = 0;
    TX3HDP_REG = 0;
    TX4HDP_REG = 0;
    TX5HDP_REG = 0;
    TX6HDP_REG = 0;
    TX7HDP_REG = 0;

    /* Clear EMAC statistics registers */
    for (i = 0; i < EMAC_STATS_REGS; i++) {
        ((uint32_t *)EMAC_STATS_BASE)[i] = 0;
    }

    /* Initialize MAC addresses
     * We use only 1 (upto 8 supported)
    */
    for (i = 0; i < 8; i++) {
        MACINDEX_REG = i;  // channel i

	if (i == 0) {
	  // higher bytes of mac address are same
	  // for all channels
	  // So needs to be programmed only once
	  temp = g_device_mac_addr[5];
	  temp |= (uint32_t)(g_device_mac_addr[4] << 8);
	  temp |= (uint32_t)(g_device_mac_addr[3] << 16);
	  temp |= (uint32_t)(g_device_mac_addr[2] << 16);
	  MACADDRHI_REG = temp;
        }

	// Program the mac low register
	temp = g_device_mac_addr[1];
	temp |= (uint32_t)(g_device_mac_addr[0] << 8);
	temp |= (i << 16);  // channel select
        temp |= (0x1 << 19); // match packets based on mac-addr
	if (i == 0) {
	    // valid bit set only for channel 0
	    temp |= (0x1 << 20);
	}

    }

    /* 
    * RX flow control
    */
    RX0FREEBUFFER_REG = NUM_RX_BUFFERS;
    RX0FLOWTHRESH_REG = RX_LOW_THRESH;

    /* MAC HASH => 0, multicasting not used */
    MACHASH1_REG = 0;
    MACHASH2_REG = 0;

    RXBUFFEROFFSET_REG = 0;  // no header room to keep in RX buffers

    RXUNICASTCLEAR_REG = 0xFF; //Disable unicast for all channels
    RXUNICASTSET_REG = 1;  // Enable unicast for channel 0

    /*
    * Multicast disabled
    * broadcast enabled on channel 0
    * No promiscous mode
    * No multi-buffered packets
    * CRC not a part of the received packet
    * QOS disabled
    * MAC control frames not sent up
    */
    RXMBPENABLE_REG = (uint32_t)(0x1 << 13 | 0x1 << 28); 

    /*
    * Full duplex
    * RX/TX flow control enabled
    * default stuff
    */
    MACCONTROL_REG = 0x8019;

    /* 
    * Disable all channel interrupts
    */
    RXINTMASKCLEAR_REG = 0xFFFF;
    TXINTMASKCLEAR_REG = 0xFF;

    /*
    * Unmask interrupts for channel 0
    */
    RXINTMASKSET_REG = 0x101;
    TXINTMASKSET_REG = 0x01;
    MACINTMASKSET_REG = 0x02; // Host error interrupt enabled

    /*
    * common initialiaztion complete
    * Now EMAC_RX/TX processes shall be spawned
    * which shall complete initialization before
    * activating the interrupts
    */

    return;
}



static inline void emac_rx_thres_isr(void)
{
   printf("RX threshold breached!!\n");

}


void dump_descriptor(pkt_desc_t *desc)
{
    printf("\n\n");
    printf("Self address: 0x%x\n", (uint32_t)desc);
    printf("Next pointer: 0x%x\n", (uint32_t)desc->next_ptr);
    printf("Buf pointer:  0x%x\n", (uint32_t)desc->buf_ptr);
    printf("Buf len:      0x%x\n", desc->buf_len);
    printf("Packet len:   0x%x\n", desc->packet_len);
    printf("Flags:        0x%x\n", desc->flags);

    printf("\n\n");

    return;
}


static inline void emac_tx_isr(void)
{
   pkt_desc_t *desc = (pkt_desc_t *)TX_DESC_MEM_BASE;
   uint16_t i = tx_ctx->buffer_done_head;


   while(!DESC_OWNED_BY_EMAC(&desc[i])) {

       if (desc[i].next_ptr == NULL) {
           // end of chain
	   i++;
	   i = i & (NUM_TX_BUFFERS - 1);
	   break;
       }

       i++;
       i = i & (NUM_TX_BUFFERS - 1);
   }

   tx_ctx->buffer_done_head = i;

   i = i - 1; // point to the last buffer
   i = i & (NUM_TX_BUFFERS - 1);
   TX0CP_REG = (uint32_t)&desc[i];

   return;
}
static inline void emac_rx_isr(void)
{
   pkt_desc_t *desc = (pkt_desc_t *)RX_DESC_MEM_BASE;
   uint16_t i = rx_ctx->head;


   while(!DESC_OWNED_BY_EMAC(&desc[i])) {
       i++;
       i = i & (NUM_RX_BUFFERS - 1);

       if (i == rx_ctx->buffer_fill_head) {
           // Whole ring has exhausted!!
	   // Application too slow
	   break;
       }
   }

   rx_ctx->tail = i;

   unlock_ksem((uint32_t)emac_rx_sem);

   i = i - 1; // point to the last buffer
   i = i & (NUM_RX_BUFFERS - 1);
   RX0CP_REG = (uint32_t)&desc[i];

   return;
}

static inline void emac_misc_isr(void)
{

  printf("Host pend interrupt!!\n");

}

void emac_c0_rx_thres(void)
{
    C0RXTHRESHEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C0_RX_THRES_INTERRUPT;

    //printf("emac_c0_rx_thres ISR called\n");
    emac_rx_thres_isr();
    
    MACEOIVECTOR_REG = 0;  // Acknowledge interrupt 
    C0RXTHRESHEN_REG = 1;  // enable interrupt for channel 0 only
}

void emac_c1_rx_thres(void)
{
    C1RXTHRESHEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C1_RX_THRES_INTERRUPT;

    //printf("emac_c1_rx_thres ISR called\n");
    emac_rx_thres_isr();

    MACEOIVECTOR_REG = 4;  // Acknowledge interrupt 
    C1RXTHRESHEN_REG = 1;
}

void emac_c0_rx(void)
{
    C0RXEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C0_RX_INTERRUPT;
    
    //printf("emac_c0_rx ISR called\n");
    
    emac_rx_isr();
    
    MACEOIVECTOR_REG = 1;  // Acknowledge interrupt 
    C0RXEN_REG = 1;
    
}

void emac_c1_rx(void)
{
    C1RXEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C1_RX_INTERRUPT;
    
    //printf("emac_c1_rx ISR called\n");
    emac_rx_isr();

    MACEOIVECTOR_REG = 5;  // Acknowledge interrupt 
    C1RXEN_REG = 1;
}

void emac_c0_tx(void)
{
    C0TXEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C0_TX_INTERRUPT;

    //printf("emac_c0_tx ISR called\n");
    emac_tx_isr();

    MACEOIVECTOR_REG = 2;  // Acknowledge interrupt 
    C0TXEN_REG = 1;
}

void emac_c1_tx(void)
{
    C1TXEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C1_TX_INTERRUPT;

    //printf("emac_c1_tx ISR called\n");
    emac_tx_isr();
    
    MACEOIVECTOR_REG = 6;  // Acknowledge interrupt 
    C1TXEN_REG = 1;
}

void emac_c0_misc(void)
{
    C0MISCEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C0_MISC_INTERRUPT;
    
    //printf("emac_c0_misc ISR called\n");
    emac_misc_isr();

    MACEOIVECTOR_REG = 3;  // Acknowledge interrupt 
    C0MISCEN_REG = 0x4;  // enable only host-pend interrupt
}

void emac_c1_misc(void)
{
    C1MISCEN_REG = 0;
    ARM_INTC_SICR = AINTC_EMAC_C1_MISC_INTERRUPT;

    //printf("emac_c1_misc ISR called\n");
    emac_misc_isr();

    MACEOIVECTOR_REG = 7;  // Acknowledge interrupt 
    C1MISCEN_REG = 0x4;  // enable only host-pend interrupt
}

